from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *

import os
import time
import re
import binascii
import threading
import configparser
import serial
import serial.tools.list_ports

from common.libs.Log import logging
from common.libs.ComDeal import ComDeal

class Serial(QLabel):
    signal_open_ok = pyqtSignal()
    signal_serial_timer = pyqtSignal()
    signal_cmd = pyqtSignal(str, str)
    # signal_recv_move = pyqtSignal(str, str)
    # signal_recv_repeat = pyqtSignal(str, str, list)
    # signal_recv_environment = pyqtSignal(str, str)
    # signal_recv_base_set = pyqtSignal(str, str)
    def __init__(self, parent=None):
        super(Serial, self).__init__(parent)
        # 信号初始化
        self.signal_serial_timer.connect(self.recvTimer)
        # 串口初始化
        self.serialInit()
        # 开启查询串口
        self.findOpenThreadStart()

        # 串口状态
        self.label_comStatus= QLabel()
        self.label_comStatus.setMinimumWidth(32)
        self.label_comStatus.setStyleSheet('image: url(:icons/images_rc/com-offline.png)')

    def serialInit(self):
        '''初始化串口'''
        self.serial = serial.Serial()
        self.serial.timeout = 0.5  # make sure that the alive event can be checked from time to time
        self.thread = None
        self.serial_alive = False
        self.serial.port = None
        self.serial.baudrate = '115200'
        self.serial.bytesize = 8
        self.serial.parity = 'N'
        self.serial.stopbits = 1
        self.serial.dtr = True
        self.serial.rts = True
        self.recv = ''

    def findOpenThreadStart(self):
        # 串口查询定时器
        self.thread_find_open = threading.Thread(target=self.findOpen)
        self.thread_find_open.setDaemon(True)
        self.thread_find_open.start()

    def findOpen(self):
        '''搜索串口并打开串口'''
        while not self.serial_alive:
            config = configparser.ConfigParser()
            config.read(os.getcwd() + '/conf/com.ini')
            # port = config['com']['device']
            # baudrate = config['com']['baudrate']
            port = ''
            baudrate = ''
            port_list = serial.tools.list_ports.comports()
            for p in port_list:
                if p.serial_number == config['com']['serial_number']:
                    port = p.device
                    baudrate = config['com']['baudrate']
                    break
            if port and baudrate:
                self.serial.port = port
                self.serial.baudrate = baudrate
                try:
                    self.serial.open()
                    logging.info('打开串口: %s-%s'%(self.serial.port, self.serial.baudrate))
                except serial.SerialException as e:
                    pass
                else:
                    self.serial_alive = True
                    self.signal_serial_timer.emit()
            else:
                pass

    def write(self, cmd_list):
        '''串口写入,十六进制格式字符串'''
        if self.serial_alive:
            if self.serial.isOpen():
                logging.info('发送命令：'+' '.join(cmd_list))
                cmd = ComDeal.getCmd(cmd_list)
                data = binascii.unhexlify(cmd)
                self.serial.write(data)
                # logging.info('发送命令：'+' '.join([cmd[i * 2:i * 2 + 2] for i in range(0, int(len(cmd) / 2))]))
        else:
            QMessageBox.information(self, '通信提示', '通信异常,请检查通信接口', QMessageBox.Yes)

    def recvTimer(self):
        """串口接收定时器"""
        self.timer_recv = QTimer()
        self.timer_recv.timeout.connect(self.receive)
        self.timer_recv.start(50)
        self.label_comStatus.setStyleSheet('image: url(:icons/images_rc/com.png)')
        self.signal_open_ok.emit()
        # self.cmdReadInfo()

    def receive(self):
        """串口接收"""
        if self.serial_alive:
            try:
                n = self.serial.inWaiting()
                if n:
                    b = self.serial.read(n)
                    # 将byte转换为十六进制字符串
                    data = str(binascii.b2a_hex(b).decode('utf-8'))
                    self.recv += data.upper()
                    logging.info('接收命令：'+' '.join([self.recv[i * 2:i * 2 + 2] for i in range(0, int(len(self.recv) / 2))]))
                    try:
                        self.serialCmdDeal()
                    except Exception as e:
                        logging.info(e)
            except:
                logging.warn("串口异常")
                self.label_comStatus.setStyleSheet('image: url(:icons/img/com-offline.png)')
                # 串口异常, 关闭串口
                if self.serial.isOpen():
                    self.serial_alive = False
                    self.serial.close()
                    # 打开串口查询线程
                    self.findOpenThreadStart()

    def serialCmdDeal(self):
        '''串口处理命令，监测校验和是否正确'''
        self.recv = self.recv
        start = self.recv.find("AA")
        startwith = self.recv.startswith('AA')
        def find_last(string, s):
            last_position = -1
            while True:
                position = string.find(s, last_position + 1)
                if position == -1:
                    return last_position
                last_position = position
        start_num = len(re.compile('.*?(AA).*?').findall(self.recv))
        if self.recv.startswith('AA'):
            for i in re.compile('.*?(AA).*?').findall(self.recv):
                # if len(self.recv)>6:
                theory_len = int(self.recv[2:4], 16)
                real_len = len(self.recv) / 2
                if theory_len <= real_len:
                    cmd = self.recv[start:theory_len * 2]
                    self.recv = self.recv[theory_len * 2:]
                    # print('截取后',self.recv)
                    cmd_start = cmd[0:2]
                    cmd_len = cmd[2:4]
                    cmd_sum = cmd[4:6]
                    if cmd_sum == (hex(int(cmd_start, 16) + int(cmd_len, 16))).upper()[-2:]:
                        cmd_cmd = cmd[6:8]
                        cmd_data = cmd[8:-2]
                        cmd_endsum = cmd[-2:]
                        s = 0
                        for i in range(0, len(cmd) - 2):
                            if i % 2 == 0:
                                s = s + int(cmd[i:i + 2], 16)
                        s = hex(s).upper()[-2:]
                        if s == cmd_endsum:
                            try:
                                self.signal_cmd.emit(cmd_cmd, cmd_data)
                            except Exception as e:
                                logging.error(e)
                        else:
                            self.recv = ''
                            logging.error('校验错误！')
                            logging.error('正确校验码:%s, 错误：%s'%(s,cmd_endsum))
                else:
                    return
        else:
            self.recv = self.recv[self.recv.find('AA'):]
